Die Boost C++ Bibliotheken
Dieses Buch ist unter einer Creative Commons-Lizenz lizensiert.
Die Bibliothek Boost.Filesystem vereinfacht das Arbeiten mit Dateien und Verzeichnissen. Sie stellt eine Klasse boost::filesystem::path zur Verfügung, mit der Pfadangaben verarbeitet werden können. Darüberhinaus existieren zahlreiche freistehende Funktionen, um zum Beispiel Verzeichnisse zu erstellen oder zu überprüfen, ob eine Datei existiert.
Die Klasse boost::filesystem::path ist die zentrale Klasse in Boost.Filesystem. Sie repräsentiert ganz allgemein Pfadangaben und bietet zahlreiche Methoden an, um Pfadangaben zu verarbeiten.
Genaugenommen handelt es sich bei boost::filesystem::path um ein typedef für boost::filesystem::basic_path<std::string>. Neben boost::filesystem::path steht boost::filesystem::wpath zur Verfügung, das ein typedef für boost::filesystem::basic_path<std::wstring> ist.
Die Headerdatei, die Sie für die Verarbeitung von Pfadangaben einbinden müssen, ist boost/filesystem.hpp. Alle Definitionen befinden sich außerdem im Namensraum boost::filesystem.
Sie können Pfadangaben bilden, indem Sie die Klasse boost::filesystem::path mit einem entsprechenden String initialisieren.
#include <boost/filesystem.hpp>
int main()
{
boost::filesystem::path p1("C:\\");
boost::filesystem::path p2("C:\\Windows");
boost::filesystem::path p3("C:\\Program Files");
}
Die Konstruktoren von boost::filesystem::path überprüfen nicht, ob die Pfadangabe Sinn macht und ob die entsprechende Datei oder das Verzeichnis existiert. Selbst mit offensichtlich unsinnigen Pfadangaben kann boost::filesystem::path instantiiert werden.
#include <boost/filesystem.hpp>
int main()
{
boost::filesystem::path p1("...");
boost::filesystem::path p2("\\");
boost::filesystem::path p3("@:");
}
Der Grund, warum obiges Programm ohne Probleme ausgeführt werden kann, ist, dass Pfadangaben einfach nur Strings sind. Die Klasse boost::filesystem::path macht also nichts anderes als Strings zu verarbeiten. Es finden keine Zugriffe aufs Dateisystem statt.
Da boost::filesystem::path Strings verarbeitet, stehen natürlich Methoden bereit, um eine Pfadangabe als String zu erhalten. Interessanterweise stehen dazu drei verschiedene Methoden zur Verfügung.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
boost::filesystem::path p("C:\\Windows\\System");
std::cout << p.string() << std::endl;
std::cout << p.file_string() << std::endl;
std::cout << p.directory_string() << std::endl;
}
Die Methode string() gibt eine sogenannte portable Pfadangabe zurück. Der String wird von Boost.Filesystem quasi normalisiert - nach ganz bestimmten Regeln, die allein für Boost.Filesystem gelten. So wird im obigen Beispiel beim Aufruf von string() C:/Windows/System zurückgeben. Das Trennzeichen von Verzeichnis- und Dateinamen, das Boost.Filesystem intern verwendet, ist offensichtlich der Slash /.
Der Sinn und Zweck von portablen Pfadangaben ist, dass sie auf allen Plattformen eindeutig Dateien und Verzeichnisse identifizieren sollen. Portable Pfadangaben sollen es möglich machen, mit einer Angabe auf Dateien und Verzeichnisse auf so unterschiedlichen Betriebssystemen wie Windows und Linux zu verweisen. Es sind dann keine Präprozessoranweisungen mehr notwendig, um den Pfad je nach Betriebssystem unterschiedlich zu kodieren. Die Regeln, nach denen portable Pfadangaben gebildet werden, finden Sie in der Referenz von Boost.Filesystem. Sie entsprechen weitgehend den Regeln des POSIX-Standards.
Beachten Sie, dass der Konstruktor von boost::filesystem::path sowohl portable als auch plattformabhängige Pfadangaben unterstützt. Die Angabe "C:\\Windows\\System", die im obigen Programm verwendet wird, ist nicht portabel, sondern Windows-spezifisch. Sie kann daher von Boost.Filesystem richtig interpretiert werden - aber nur, wenn das Programm unter Windows ausgeführt wird! Das gleiche Programm auf einem POSIX-System wie Linux ausgeführt gibt für string() C:\Windows\System zurück. Denn in diesem Fall erkennt Boost.Filesystem nicht, dass der Backslash \ als Trennzeichen für Dateien und Verzeichnisse verwendet werden soll. Denn er ist weder im portablen noch im nativen Format unter Linux ein Trennzeichen.
Häufig wird es notwendig sein, plattformabhängige Pfadangaben als String zu erhalten. Das ist zum Beispiel immer dann der Fall, wenn Sie eine Betriebssystemfunktion aufrufen wollen, die eine plattformabhängige Kodierung erwartet. Genau für diesen Fall sind die Methoden file_string() und directory_string() gedacht.
Im obigen Beispiel geben beide Methoden C:\Windows\System zurück - sowohl unter Windows als auch auf einem POSIX-System wie Linux. Unter Windows handelt sich schließlich um eine gültige Pfadangabe. Unter Linux ist wie bereits erklärt der Pfad, mit dem boost::filesystem::path initialisiert wurde, weder als portabel noch als plattformabhängig erkannt worden.
Im Folgenden findet die Initialisierung mit einem portablen Pfad statt.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
boost::filesystem::path p("/");
std::cout << p.string() << std::endl;
std::cout << p.file_string() << std::endl;
std::cout << p.directory_string() << std::endl;
}
Da string() einen portablen Pfad zurückgibt, ist das in diesem Fall genau die Angabe, mit der boost::filesystem::path initialisiert wurde - also /. Die Methoden file_string() und directory_string() liefern jedoch je nach Plattform unterschiedliche Werte zurück. Unter Windows wird von beiden Methoden \, unter Linux / zurückgegeben.
Vielleicht wundern Sie sich, warum es zwei Methoden gibt, die Pfadangaben in einer plattformabhängigen Kodierung zurückgeben. Immerhin wurden in den bisherigen Beispielen jeweils die gleichen Werte von file_string() und directory_string() zurückgegeben. Es gibt aber Betriebssysteme, die unterschiedliche Werte zurückgeben können. Da Boost.Filesystem möglichst viele Betriebssysteme unterstützen will, gibt es entsprechend zwei Methoden. Auch wenn Sie eher mit Windows oder POSIX-Systemen wie Linux vertraut sind und auf diesen Plattformen jeweils die gleichen Werte zurückgegeben werden, ist es empfehlenswert, Pfade für Dateien mit file_string() und für Verzeichnisse mit directory_string() zu erhalten. Das erhöht die Portabilität Ihres Codes.
boost::filesystem::path bietet mehrere Methoden an, um auf bestimmte Teile eines Pfads zuzugreifen.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
boost::filesystem::path p("C:\\Windows\\System");
std::cout << p.root_name() << std::endl;
std::cout << p.root_directory() << std::endl;
std::cout << p.root_path() << std::endl;
std::cout << p.relative_path() << std::endl;
std::cout << p.parent_path() << std::endl;
std::cout << p.filename() << std::endl;
}
Wenn obiges Programm unter Windows ausgeführt wird, wird der String "C:\\Windows\\System" als plattformabhängige Pfadangabe interpretiert. Daraufhin gibt root_name() C: zurück, root_directory() /, root_path() C:/, relative_path() Windows/System, parent_path() C:/Windows und filename() System.
Wie Sie sehen erhalten Sie keine plattformabhängigen Pfadangaben. So enthalten die zurückgegebenen Werte keine Backslashs \, sondern Slashs /. Sie müssen, wenn Sie plattformabhängige Pfadangaben benötigen, also immer file_string() oder directory_string() verwenden. Um diese Methoden auf Teile eines Pfads anwenden zu können, müssen Sie ein neues Objekt vom Typ boost::filesystem::path erstellen, das Sie entsprechend initialisieren.
Wenn Sie obiges Programm unter Linux ausführen, erhalten Sie andere Werte. So geben fast alle Methoden eine leere Zeichenkette zurück. Einzige Ausnahme sind relative_path() und filename(), die jeweils C:\Windows\System zurückgeben. Die Angabe "C:\\Windows\\System" wird demnach unter Linux als ein Dateiname interpretiert. Das ist insofern verständlich als dass es sich hierbei weder um eine portable Kodierung einer Pfadangabe noch um eine von Linux unterstützte plattformabhängige Kodierung handelt. Boost.Filesystem bleibt demnach unter Linux nichts anderes übrig als die gesamte Angabe als einen Dateinamen zu interpretieren.
Boost.Filesystem bietet zusätzlich Methoden an, mit denen sich ermitteln lässt, ob das betreffende Teilstück in einem Pfad existiert. Diese Methoden lauten has_root_name(), has_root_directory(), has_root_path(), has_relative_path(), has_parent_path() und has_filename(). Sie alle haben einen Rückgabewert vom Typ bool.
Es gibt zwei weitere Methoden, mit denen ein Dateiname in seine Bestandteile zerlegt wird. Diese Methoden sollten nur dann angewandt werden, wenn has_filename() true zurückgibt. Ansonsten werden leere Zeichenketten zurückgegeben - wo kein Dateiname, kann schließlich nichts zerlegt werden.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
boost::filesystem::path p("photo.jpg");
std::cout << p.stem() << std::endl;
std::cout << p.extension() << std::endl;
}
Obiges Programm gibt für stem() photo und für extension() .jpg zurück.
Anstatt Bestandteile eines Pfads über Methodenaufrufe abzugreifen, kann auch über diese Bestandteile iteriert werden.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
boost::filesystem::path p("C:\\Windows\\System");
for (boost::filesystem::path::iterator it = p.begin(); it != p.end(); ++it)
std::cout << *it << std::endl;
}
Obiges Programm gibt nacheinander C:, /, Windows und System aus - natürlich nur, wenn das Programm unter Windows ausgeführt wird. Auf einem anderen Betriebssystem wie Linux wird die Angabe C:\Windows\System komplett ausgegeben.
Nachdem Sie mehrere Methoden kennengelernt haben, um auf verschiedene Bestandteile eines Pfads zuzugreifen, lernen Sie im Folgenden eine Methode kennen, mit der eine Pfadangabe geändert werden kann.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
boost::filesystem::path p("C:\\");
p /= "Windows\\System";
std::cout << p.string() << std::endl;
}
Im obigen Programm wird mit Hilfe des überladenen Operators operator/=() ein Pfad an einen anderen angehängt. Das Programm gibt daher C:/Windows/System aus - unter Windows. Unter Linux wird C:\/Windows\System ausgegeben, da unter Linux der Slash / das Trennzeichen für Dateien und Verzeichnisse ist. Dieser Slash ist auch der Grund, warum der Operator operator/=() überladen wurde: Immerhin steckt der Slash hier im Methodennamen.
Neben operator/=() bietet Boost.Filesystem zum Ändern einer Pfadangabe lediglich die Methoden remove_filename() und replace_extension() an.
Die Methoden, die Ihnen im Zusammenhang mit der Klasse boost::filesystem::path vorgestellt wurden, tun nichts anderes als Strings verarbeiten. So können Sie auf Teilbereiche eines Pfads zugreifen oder einen Pfad an einen anderen anhängen. Intern werden in der Klasse boost::filesystem::path einfach nur Strings verarbeitet.
Um nun auch tatsächlich auf Dateien und Verzeichnisse auf der Festplatte zugreifen zu können, stehen zahlreiche freistehende Funktionen zur Verfügung. Diese erwarten alle als Parameter ein oder mehrere Objekte vom Typ boost::filesystem::path und rufen dann für die derart identifizierten Dateien oder Verzeichnisse Betriebssystemfunktionen auf.
Bevor verschiedene Funktionen zum Datei- und Verzeichniszugriff vorgestellt werden, erfahren Sie, was im Falle eines Fehlers passiert. Denn alle Funktionen, die Sie kennenlernen werden, greifen auf Betriebssystemfunktionen zu, die fehlschlagen können. Tritt ein Fehler auf, wird eine Ausnahme vom Typ boost::filesystem::filesystem_error geworfen. Diese Klasse ist von boost::system::system_error abgeleitet und fügt sich somit ins Framework von Boost.System ein.
Abgesehen von den Methoden what() und code(), die von der Elternklasse boost::system::system_error geerbt werden, stehen zwei zusätzliche Methoden path1() und path2() zur Verfügung. Beide geben ein Objekt vom Typ boost::filesystem::path zurück. Da es Funktionen gibt, die als Parameter zwei Objekte vom Typ boost::filesystem::path erwarten, stehen entsprechend zwei Methoden zur Verfügung. So können im Fehlerfall die Pfadangaben einfach ermittelt werden.
Viele Funktionen liegen in zwei Varianten vor, von denen eine im Fehlerfall eine Ausnahme vom Typ boost::filesystem::filesystem_error wirft und die andere den Fehler als Objekt vom Typ boost::system::error_code zurückgibt. In diesem Fall muss explizit der Rückgabewert ausgewertet werden, um festzustellen, ob es einen Fehler gab oder nicht.
Im Folgenden lernen Sie eine Funktion kennen, mit der der Status einer Datei oder eines Verzeichnisses abgefragt werden kann.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
boost::filesystem::path p("C:\\");
try
{
boost::filesystem::file_status s = boost::filesystem::status(p);
std::cout << boost::filesystem::is_directory(s) << std::endl;
}
catch (boost::filesystem::filesystem_error &e)
{
std::cerr << e.what() << std::endl;
}
}
Die Funktion boost::filesystem::status() gibt ein Objekt vom Typ boost::filesystem::file_status zurück. Sie können dieses Objekt an verschiedene Hilfsfunktionen übergeben, um es auszuwerten. So gibt boost::filesystem::is_directory() true zurück, wenn der Status für ein Verzeichnis ermittelt wurde. Neben boost::filesystem::is_directory() stehen weitere Funktionen wie boost::filesystem::is_regular_file(), boost::filesystem::is_symlink() und boost::filesystem::exists() zur Verfügung, die alle einen Rückgabewert vom Typ bool besitzen.
Neben boost::filesystem::status() existiert übrigens eine zweite Funktion boost::filesystem::symlink_status(). Diese kann eingesetzt werden, wenn der Status einer Datei erhalten werden soll, die eine symbolische Verknüpfung ist. Wenn für eine derartige Datei boost::filesystem::status() aufgerufen wird, wird der Status für die Datei ermittelt, auf die die symbolische Verknüpfung verweist. Unter Windows sind Dateien, die symbolische Verknüpfungen darstellen, an der Endung lnk zu erkennen.
Neben den eben vorgestellten Status-Funktionen gibt es eine andere Kategorie von Funktionen, mit denen Attribute ermittelt werden können.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
boost::filesystem::path p("C:\\Windows\\win.ini");
try
{
std::cout << boost::filesystem::file_size(p) << std::endl;
}
catch (boost::filesystem::filesystem_error &e)
{
std::cerr << e.what() << std::endl;
}
}
Die Funktion boost::filesystem::file_size() gibt die Größe einer Datei in Bytes zurück.
#include <boost/filesystem.hpp>
#include <iostream>
#include <ctime>
int main()
{
boost::filesystem::path p("C:\\Windows\\win.ini");
try
{
std::time_t t = boost::filesystem::last_write_time(p);
std::cout << std::ctime(&t) << std::endl;
}
catch (boost::filesystem::filesystem_error &e)
{
std::cerr << e.what() << std::endl;
}
}
Um den Zeitpunkt zu ermitteln, wann eine Datei das letzte Mal geändert wurde, kann boost::filesystem::last_write_time() aufgerufen werden.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
boost::filesystem::path p("C:\\");
try
{
boost::filesystem::space_info s = boost::filesystem::space(p);
std::cout << s.capacity << std::endl;
std::cout << s.free << std::endl;
std::cout << s.available << std::endl;
}
catch (boost::filesystem::filesystem_error &e)
{
std::cerr << e.what() << std::endl;
}
}
Mit der Funktion boost::filesystem::space() kann der insgesamt zur Verfügung stehende und momentan noch freie Speicherplatz erhalten werden. Die Funktion gibt ein Objekt vom Typ boost::filesystem::space_info zurück, das drei öffentliche Eigenschaften capacity, free und available definiert. Alle drei Eigenschaften sind vom Typ boost::uintmax_t. Dieser Typ ist in der Bibliothek Boost.Integer definiert und ist typischerweise ein typedef für unsigned long long. Der Speicherplatz wird demnach in Bytes gemessen.
Während die bisher vorgestellten Funktionen Dateien und Verzeichnisse unberührt lassen, existieren zahlreiche Funktionen, mit denen Dateien und Verzeichnisse erstellt, umbenannt oder gelöscht werden können.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
boost::filesystem::path p("C:\\Test");
try
{
if (boost::filesystem::create_directory(p))
{
boost::filesystem::rename(p, "C:\\Test2");
boost::filesystem::remove("C:\\Test2");
}
}
catch (boost::filesystem::filesystem_error &e)
{
std::cerr << e.what() << std::endl;
}
}
Obiges Programm sollte selbsterklärend sein. Wenn Sie genau hinsehen, stellen Sie aber fest, dass den verschiedenen Funktionen nicht immer Objekte vom Typ boost::filesystem::path übergeben werden, sondern teilweise auch einfach nur Strings. Das ist insofern kein Problem als dass boost::filesystem::path einen nicht-expliziten Konstruktor anbietet, so dass Strings automatisch in Objekte vom Typ boost::filesystem::path umgewandelt werden. Das erleichtert die Anwendung der vielen Funktionen von Boost.Filesystem, da nicht selbst explizit Objekte erstellt werden müssen.
Neben den eben im Beispiel verwendeten Funktionen stehen auch Funktionen wie create_symlink() zur Verfügung, mit der sich symbolische Verknüpfungen erstellen lassen, oder copy_file(), mit der eine Datei oder ein Verzeichnis kopiert werden kann.
Als nächstes wird Ihnen eine Funktion vorgestellt, die ausgehend von einem Dateinamen oder Teilausschnitt eines Pfads einen absoluten Pfad erstellt.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
try
{
std::cout << boost::filesystem::complete("photo.jpg") << std::endl;
}
catch (boost::filesystem::filesystem_error &e)
{
std::cerr << e.what() << std::endl;
}
}
Welcher Pfad vom obigen Programm ausgegeben wird, hängt davon ab, in welchem Verzeichnis das Programm gestartet wird. Wird es zum Beispiel in C:\ ausgeführt, gibt obiges Programm C:/photo.jpg aus.
Beachten Sie wieder den Slash /! Möchten Sie einen plattformabhängigen Pfad, müssen Sie ein Objekt vom Typ boost::filesystem::path initialisieren und file_string() aufrufen.
Wenn Sie einen absoluten Pfad relativ zu einem anderen Verzeichnis erhalten möchten, können Sie boost::filesystem::complete() einen zweiten Parameter übergeben.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
try
{
std::cout << boost::filesystem::complete("photo.jpg", "D:\\") << std::endl;
}
catch (boost::filesystem::filesystem_error &e)
{
std::cerr << e.what() << std::endl;
}
}
Obiges Programm gibt D:/photo.jpg aus.
Abschließend soll Ihnen eine nützliche Hilfsfunktion vorgestellt werden, mit der sich das aktuelle Arbeitsverzeichnis eines Programms ermitteln lässt.
#include <windows.h>
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
try
{
std::cout << boost::filesystem::current_path() << std::endl;
SetCurrentDirectory("C:\\");
std::cout << boost::filesystem::current_path() << std::endl;
}
catch (boost::filesystem::filesystem_error &e)
{
std::cerr << e.what() << std::endl;
}
}
Obiges Programm kann nur unter Windows ausgeführt werden, da die Windows-Funktion SetCurrentDirectory() verwendet wird. Diese Funktion ändert das Arbeitsverzeichnis, so dass die beiden Aufrufe von boost::filesystem::current_path() unterschiedliche Ergebnisse zurückgeben.
Neben boost::filesystem::current_path() bietet Boost.Filesystem eine Funktion boost::filesystem::initial_path() an, die das Arbeitsverzeichnis zurückgibt, in dem ein Programm ursprünglich gestartet wurde. Da diese Funktion von zusätzlicher Betriebssystemunterstützung abhängt, kann ihr Einsatz für portable Anwendungen nicht empfohlen werden. Stattdessen sollte wie in der Dokumentation von Boost.Filesystem empfohlen zu Beginn des Programms der Rückgabewert von boost::filesystem::current_path() gespeichert werden, wenn dieser Wert später im Programm verwendet werden muss.
Im C++ Standard sind verschiedene Dateistreams in der Headerdatei fstream definiert. Diese Streams akzeptieren natürlich keine Parameter vom Typ boost::filesystem::path. Da die Bibliothek Boost.Filesystem aller Voraussicht nach im Technical Report 2 des C++ Standards aufgenommen werden wird, werden die Dateistreams um entsprechende Konstruktoren erweitert werden. Wer heute bereits Dateistreams mit Pfadangaben vom Typ boost::filesystem::path öffnen möchte, kann dazu auf die Headerdatei boost/filesystem/fstream.hpp zugreifen. Diese Headerdatei nimmt das vorweg, was mit dem Technical Report 2 eines Tages im C++ Standard enthalten sein wird.
#include <boost/filesystem/fstream.hpp>
#include <iostream>
int main()
{
boost::filesystem::path p("test.txt");
boost::filesystem::ofstream ofs(p);
ofs << "Hello, world!" << std::endl;
}
Nicht nur die Konstruktoren der Dateistreams, sondern natürlich auch die überladenen Methoden open() akzeptieren Parameter vom Typ boost::filesystem::path.
Sie können die Lösungen zu allen Aufgaben in diesem Buch als ZIP-Datei erwerben.
Erstellen Sie ein Programm, das einen absoluten Pfad für eine Datei data.txt erstellt, die in einem Verzeichnis liegt, das sich eine Ebene über dem Arbeitsverzeichnis des Programms befindet. Wenn Sie das Programm zum Beispiel im Verzeichnis C:\Programme\Test ausführen, soll der Pfad C:\Programme\data.txt ausgegeben werden.
Copyright © 2008-2010 Boris Schäling